package com.idanchuang.component.businesslog.es.client;

import com.idanchuang.component.businesslog.es.config.RestClientConfigBean;
import com.idanchuang.component.businesslog.es.index.CustomRestHighLevelClient;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

/**
 * <p>
 * RestClientFactoryBean
 * </p>
 *
 * @author jiang
 * @date 2019-11-27 12:08
 */
public class RestClientFactoryBean implements FactoryBean<RestHighLevelClient>, InitializingBean, DisposableBean {

    private static final Logger log = LoggerFactory.getLogger(RestClientFactoryBean.class);

    private RestClientConfigBean clientConfigBean;
    private RestHighLevelClient client;
    private List<String> uris;
    private String username;
    private String password;

    @Override
    public void destroy() throws IOException {
        if (null != client) {
            client.close();
        }
    }

    @Override
    public void afterPropertiesSet() {
        buildClient();
    }

    @Override
    public RestHighLevelClient getObject() {
        return client;
    }

    @Override
    public Class<?> getObjectType() {
        return RestHighLevelClient.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    private void buildClient() {
        Assert.hasText(uris.toString(), "[Assertion Failed] At least one uri must be set.");

        HttpHost[] hosts = uris.stream().map(uri -> {
            URL hostUrl;
            try {
                hostUrl = new URL(uri);
            } catch (MalformedURLException e) {
                throw new RuntimeException("Error while build client: " + uris.toString(), e);
            }
            return new HttpHost(hostUrl.getHost(), hostUrl.getPort(), hostUrl.getProtocol());
        }).toArray(HttpHost[]::new);

        RestClientBuilder builder = RestClient.builder(hosts);
        // 身份认证
        if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
            builder.setHttpClientConfigCallback(callback -> callback.setDefaultCredentialsProvider(credentialsProvider)
                    .setKeepAliveStrategy((response, context) -> clientConfigBean.getKeepAliveTime()));
        } else {
            builder.setHttpClientConfigCallback(callback -> callback.setKeepAliveStrategy((response, context) -> clientConfigBean.getKeepAliveTime()));
        }
        builder.setRequestConfigCallback(requestConfigBuilder -> requestConfigBuilder
                .setConnectTimeout(clientConfigBean.getConnectTimeout())
                        .setSocketTimeout(clientConfigBean.getSocketTimeout()));
        client = new CustomRestHighLevelClient(builder);
        log.info("Elasticsearch client init success. uri(s):[{}]", uris);
    }

    public void setUris(List<String> uris) {
        this.uris = uris;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public RestClientConfigBean getClientConfigBean() {
        return clientConfigBean;
    }

    public void setClientConfigBean(RestClientConfigBean clientConfigBean) {
        this.clientConfigBean = clientConfigBean;
    }
}
